# ======================================================================
# Copyright CERFACS (June 2019)
# Contributor: Adrien Suau (adrien.suau@cerfacs.fr)
#
# This software is governed by the CeCILL-B license under French law and
# abiding  by the  rules of  distribution of free software. You can use,
# modify  and/or  redistribute  the  software  under  the  terms  of the
# CeCILL-B license as circulated by CEA, CNRS and INRIA at the following
# URL "http://www.cecill.info".
#
# As a counterpart to the access to  the source code and rights to copy,
# modify and  redistribute granted  by the  license, users  are provided
# only with a limited warranty and  the software's author, the holder of
# the economic rights,  and the  successive licensors  have only limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading,  using, modifying and/or  developing or reproducing  the
# software by the user in light of its specific status of free software,
# that  may mean  that it  is complicated  to manipulate,  and that also
# therefore  means that  it is reserved for  developers and  experienced
# professionals having in-depth  computer knowledge. Users are therefore
# encouraged  to load and  test  the software's  suitability as  regards
# their  requirements  in  conditions  enabling  the  security  of their
# systems  and/or  data to be  ensured and,  more generally,  to use and
# operate it in the same conditions as regards security.
#
# The fact that you  are presently reading this  means that you have had
# knowledge of the CeCILL-B license and that you accept its terms.
# ======================================================================

from qat.lang.AQASM.gates import X

from qat.lang.AQASM.routines import QRoutine
from qat.lang.AQASM.misc import build_gate


class Oracle:
    pass


def _check_uint_value(val: int, int_size: int):
    """Assert that the given `int_size` is enough to encode `val`.

    :param val: The value to test.
    :param int_size: The number of bits used to encode `val`.
    :raise AssertError: when the `int_size` is not enough to encode `val` without loss
        of information.
    """
    assert val < 2 ** int_size, (
        "The given int_size ({0}, i.e. a maximum value of "
        "2**{0} - 1 = {1}) is not sufficient to encode the given value ({2})."
    ).format(int_size, 2 ** int_size - 1, val)


@build_gate("encode_uint_big_endian", [int, int], lambda v, int_size: int_size)
def encode_uint_big_endian(val: int, int_size: int) -> QRoutine:
    """Construct a quantum circuit encoding `val` in MSB on `int_size` qubits.

    :param val: The value to encode.
    :param int_size: The number of bits used to encode `val`.
    :raise AssertError: when the `int_size` is not enough to encode `val` without loss
        of information.
    :return: a circuit encoding `val` on `int_size` qubits.
    """
    _check_uint_value(val, int_size)
    rout = QRoutine()
    qubits = rout.new_wires(int_size)
    max_mask = 1 << (int_size - 1)
    for bit_pos_big_endian in range(int_size):
        mask = max_mask >> bit_pos_big_endian
        if val & mask:
            rout.apply(X, qubits[bit_pos_big_endian])
    return rout


@build_gate("encode_uint_little_endian", [int, int], lambda v, int_size: int_size)
def encode_uint_little_endian(val: int, int_size: int) -> QRoutine:
    """Construct a quantum circuit encoding `val` in LSB on `int_size` qubits.

    :param val: The value to encode.
    :param int_size: The number of bits used to encode `val`.
    :raise AssertError: when the `int_size` is not enough to encode `val` without loss
        of information.
    :return: a circuit encoding `val` on `int_size` qubits.
    """
    _check_uint_value(val, int_size)
    rout = QRoutine()
    qubits = rout.new_wires(int_size)
    for bit_pos_little_endian in range(int_size):
        mask = 1 << bit_pos_little_endian
        if val & mask:
            rout.apply(X, qubits[bit_pos_little_endian])
    return rout
